package com.cloxtec.scada.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.cloxtec.scada.utils.MyCaptcha;
import com.cloxtec.scada.utils.ParamHelper;
import com.ramostear.captcha.common.Fonts;
import com.ramostear.captcha.support.CaptchaType;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.*;

@Component
@Slf4j
public class UserManager {

    @Resource
    private MqttPubClient mqttPubClient;

    @Resource
    private ParamHelper paramHelper;

    private JSONObject users;

    private Timer userOnlineTimer;

    boolean bInited;

    public UserManager() {
        bInited = false;
        userOnlineTimer = new Timer();
        userOnlineTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                try {
                    refreshClientOnlineCounter();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }, 1000, 5000);//1秒后开始， 5秒启动一次，
    }

    private void getClientUsers() {
        JSONObject client = paramHelper.getParamById("snapshot", "client");
        if (client == null) {
            client = new JSONObject();
            users = new JSONObject();
            client.put("_id", "client");
            client.put("users", users);
            paramHelper.saveParam("snapshot", client);
        } else {
            users = client.getJSONObject("users");
        }
    }

    private void setClientUsers() {
        JSONObject client = paramHelper.getParamById("snapshot", "client");
        if (client == null) {
            client = new JSONObject();
            client.put("_id", "client");
            client.put("users", users);
            paramHelper.saveParam("snapshot", client);
        } else {
            client.put("users", users);
            paramHelper.updateParam("snapshot", client);
        }
    }

    public boolean isClientExist(String clientId) {
        if ((clientId == null) || clientId == "") {
            return false;
        }
        if (users.containsKey(clientId)) {
            return true;
        } else {
            return false;
        }
    }

    public void setClientOnlineStatus(String clientId, boolean online) {
        if (users.containsKey(clientId)) {
            users.getJSONObject(clientId).put("online", online);
            if (!online) { // 不在线
                if (!users.getJSONObject(clientId).getBooleanValue("isLogin")) {// 没有登录
                    log.info("clientId:" + clientId + ", 未登录，且不在线，系统将删除 ");
                    removeClient(clientId, true);
                }
            }
        }
    }

    public void resetClientHeartbeat(String clientId) {
        if (users.containsKey(clientId)) {
            users.getJSONObject(clientId).put("heartbeatCounter", 0);
        }
    }

    public void setClientLoginStatus(String clientId, boolean login) {
        if (users.containsKey(clientId)) {
            users.getJSONObject(clientId).put("isLogin", login);
            setClientUsers();
        }
    }

    public void refreshClientOnlineCounter() {
        if (!bInited) {
            getClientUsers();
            bInited = true;
        }

        Set<String> clientKeys = users.keySet();
        if (clientKeys.isEmpty()) {
            return;
        }

        for (String clientId : clientKeys) {
            JSONObject client = users.getJSONObject(clientId);
            if (client != null) {

                int nCounter = client.getIntValue("heartbeatCounter");
                nCounter++;
                client.put("heartbeatCounter", nCounter);

                nCounter = client.getIntValue("verifyCodeCounter");
                nCounter++;
                client.put("verifyCodeCounter", nCounter);

                if (client.getBooleanValue("online") && !client.getBooleanValue("isLogin")) {
                    if (client.getIntValue("verifyCodeCounter") >= 30) {// 在线，但未登录，每隔30秒重置验证码
                        client.put("verifyCodeCounter", 0);
                        resetCaptcha(clientId);
                    }
                }

//                if (client.getIntValue("heartbeatCounter") >= 300) {
//                    log.info("clientId:"+clientId+", 超过300秒未收到心跳报文，系统将删除");
//                    removeClient(clientId, true);
//                }
            }
        }
    }

    public void resetCaptcha(String clientId) {
        MyCaptcha captcha = new MyCaptcha();
        captcha.setType(CaptchaType.NUMBER);
        captcha.setWidth(160);
        captcha.setHeight(50);
        captcha.setLength(4);
        captcha.setFont(Fonts.getInstance().defaultFont());
        String verifyCode = captcha.getCaptchaCode();
        String strCaptchaImg = captcha.toBase64();

        setClientVerifyCode(clientId, verifyCode);

        JSONObject jsonReturn = new JSONObject();
        jsonReturn.put("webMsgType", 1002);
        jsonReturn.put("clientId", clientId);
        jsonReturn.put("retCode", 0);
        jsonReturn.put("retMsg", "重发校验码...");
        jsonReturn.put("data", new JSONObject());
        jsonReturn.getJSONObject("data").put("captchaImg", strCaptchaImg);

        mqttPubClient.publishMsg(clientId, jsonReturn);
    }

    public boolean setClientUsername(String clientId, String username) {
        if (users.containsKey(clientId)) {
            users.getJSONObject(clientId).put("username", username);
            setClientUsers();
            return true;
        }
        return false;
    }

    public String getClientUsername(String clientId) {
        if (users.containsKey(clientId)) {
            return users.getJSONObject(clientId).getString("username");
        }
        return null;
    }

    public JSONArray getUsers() {
        JSONArray data = new JSONArray();
        Set<String> keys = users.keySet();
        for (String key : keys) {
            JSONObject user = JSONObject.parseObject(users.getJSONObject(key).toJSONString());
            if (user.containsKey("verifyCode")) {
                user.remove("verifyCode");
            }
            if (user.getIntValue("heartbeatCounter") >= 300) {
                user.put("isOnline", false);
            } else {
                user.put("isOnline", true);
            }
            user.put("clientId", key);
            data.add(user);
        }

        return data;
    }

    public void addClient(String clientId) {
        if (clientId == null || clientId == "") {
            return;
        }
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");// 格式化日期
        String strDatetime = sdf.format(date);

        users.put(clientId, new JSONObject());
        users.getJSONObject(clientId).put("online", true);
        users.getJSONObject(clientId).put("isLogin", false);
        users.getJSONObject(clientId).put("heartbeatCounter", 0);
        users.getJSONObject(clientId).put("verifyCodeCounter", 0);
        users.getJSONObject(clientId).put("update_time", strDatetime);
        setClientUsers();
    }

    public void setClientVerifyCode(String clientId, String verifyCode) {
        if (users.containsKey(clientId)) {
            users.getJSONObject(clientId).put("verifyCode", verifyCode);
            users.getJSONObject(clientId).put("isLogin", false);
        }
    }

    public void removeClient(String clientId, boolean bForce) {
        JSONObject client = users.getJSONObject(clientId);
        if (client != null) {
            String username = client.getString("username");
            users.remove(clientId);
            log.info("remove client:" + clientId);
            if (bForce && (username != null)) {
                Date date = new Date();
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");// 格式化日期
                String strDatetime = sdf.format(date);
                paramHelper.updateParam("param_user", username, "lastLogout", strDatetime);
                paramHelper.updateParam("param_user", username, "isLogin", false);
            }

            setClientUsers();
        }
        broadcastChangedClients();
    }

    public void broadcastChangedClients() {
        JSONObject jsonReturn = new JSONObject();
        jsonReturn.put("webMsgType", 1021);
        jsonReturn.put("funCode", 0);
        jsonReturn.put("retCode", 0);
        jsonReturn.put("retMsg", "");
        jsonReturn.put("data", getUsers());
        mqttPubClient.publishMsg("yg_scada_service_send_msg", jsonReturn);
    }

    public String getClientVerifyCode(String clientId) {
        if (users.containsKey(clientId)) {
            return users.getJSONObject(clientId).getString("verifyCode");
        }
        return "---";
    }

    public boolean onLogin(String clientId, String userId, String password, JSONObject user) {
        boolean bLogin = false;
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");// 格式化日期
        String strDatetime = sdf.format(date);
        String userPassword = user.getString("password");
log.info("userId:"+userId+",userPassword:"+userPassword+",password:"+password);
        if (userId.equals("admin") && (userPassword.length() == 0)) {
            paramHelper.updateParam("param_user", userId, "password", password);
            bLogin = true;
        } else if (password.equals(userPassword)) {
            bLogin = true;
        } else {
            //log.info("received password:" + password, "correct password=" + userPassword);
        }

        if (bLogin) {
            setClientUsername(clientId, userId);
            setClientLoginStatus(clientId, true);
            paramHelper.updateParam("param_user", userId, "lastLogin", strDatetime);
            paramHelper.updateParam("param_user", userId, "isLogin", true);
        }

        setClientUsers();

        return bLogin;
    }

    public boolean onLogout(String clientId) {
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");// 格式化日期
        String strDatetime = sdf.format(date);
        String username = getClientUsername(clientId);
        if (username != null) {
            paramHelper.updateParam("param_user", username, "lastLogout", strDatetime);
            paramHelper.updateParam("param_user", username, "isLogin", false);
        }

        setClientUsername(clientId, "");
        setClientLoginStatus(clientId, false);

        setClientUsers();
        return true;
    }

    public boolean isClientLogin(String clientId) {
        boolean bLogin = false;
        if (users.containsKey(clientId)) {
            JSONObject user = users.getJSONObject(clientId);
            bLogin = user.getBooleanValue("isLogin");
        }
        return bLogin;
    }
}
